package me.boger.venusplayer.view

import android.animation.Animator
import android.animation.AnimatorListenerAdapter
import android.animation.ObjectAnimator
import android.animation.ValueAnimator
import android.content.Context
import android.graphics.Canvas
import android.graphics.Color
import android.graphics.Paint
import android.graphics.Rect
import android.util.AttributeSet
import android.view.MotionEvent
import android.view.View
import android.view.animation.BounceInterpolator
import me.boger.venusplayer.inject.toPixelFromDIP

/**
 * Created by hb.chen on 2017/10/12.
 */
class VideoSeekBar : View {

    companion object {
        val DEFAULT_INDICATOR_DP_WIDTH = 8F
        val DEFAULT_INDICATOR_DP_HEIGHT = 12F
        val DEFAULT_BORDER_HEIGHT = 8
        val TOUCH_AREA_EXPAND_SIZE = 5F
    }

    private var mBorderColor = Color.CYAN
    private var mSolidColor = Color.RED
    private var mIndicatorColor = Color.YELLOW
    private val mPaint by lazy { Paint(Paint.ANTI_ALIAS_FLAG) }
    private var mBorderRect = Rect()
    private var mSolidRect = Rect()
    private var mIndicatorRect = Rect()
    private var mBorderSize = 0f
    private var mTouchAnimator: Animator? = null

    var seekBarChangeListener: ((seekBar: VideoSeekBar, progress: Float, fromUser: Boolean) -> Unit)? = null

    var position: Float = 0f
        set(value) {
            field = value
            mSolidRect.right = ((mBorderRect.right - 2) * field).toInt()
            mIndicatorRect.offsetTo((mBorderRect.right * field - mIndicatorRect.width() / 2).toInt(), mIndicatorRect.top)
        }

    constructor(context: Context) : super(context) {
        mBorderSize = context.toPixelFromDIP(1f)
    }

    constructor(context: Context, attrs: AttributeSet) : super(context, attrs) {
        // TODO attrs support
        mBorderSize = context.toPixelFromDIP(1f)
    }

    override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec)
        val borderPixelHeight = context.toPixelFromDIP(DEFAULT_BORDER_HEIGHT.toFloat()).toInt()
        mBorderRect.set(0, 0, measuredWidth - paddingLeft - paddingRight - (mBorderSize * 2).toInt(), borderPixelHeight)
        mBorderRect.offsetTo(paddingLeft, (measuredHeight - paddingBottom + paddingTop - borderPixelHeight) / 2)
        mSolidRect.set(mBorderRect.left + 2, mBorderRect.top + 2, mBorderRect.left + 2, mBorderRect.bottom - 2)
        val indicatorPixelWidth = context.toPixelFromDIP(DEFAULT_INDICATOR_DP_WIDTH).toInt()
        val indicatorPixelHeight = context.toPixelFromDIP(DEFAULT_INDICATOR_DP_HEIGHT).toInt()
        mIndicatorRect.set(0, 0, indicatorPixelWidth, indicatorPixelHeight)
        mIndicatorRect.offsetTo(mBorderRect.left - indicatorPixelWidth / 2,
                (mBorderRect.top + mBorderRect.bottom - indicatorPixelHeight) / 2)
        position = 0.4f
    }

    override fun onDraw(canvas: Canvas?) {
        if (canvas == null) {
            return
        }
        // draw border line
        mPaint.color = mBorderColor
        mPaint.style = Paint.Style.STROKE
        mPaint.strokeWidth = mBorderSize
        canvas.drawRect(mBorderRect, mPaint)
        // draw solid line
        mPaint.color = mSolidColor
        mPaint.style = Paint.Style.FILL
        canvas.drawRect(mSolidRect, mPaint)
        // draw indicator object
        mPaint.color = mIndicatorColor
        canvas.drawRect(mIndicatorRect, mPaint)
    }

    override fun onTouchEvent(event: MotionEvent?): Boolean {
        event ?: return true
        when (event.actionMasked) {
            MotionEvent.ACTION_DOWN -> {
                if (!inIndicatorArea(event.x, event.y)) {
                    return false
                }
                startTouchAnimator()
            }
            MotionEvent.ACTION_UP -> stopTouchAnimator()
            MotionEvent.ACTION_CANCEL -> stopTouchAnimator()
            MotionEvent.ACTION_MOVE -> {
                var pos = event.x / measuredWidth * scaleX
                pos = Math.max(0F, Math.min(1F, pos))
                position = pos
                invalidate()
                seekBarChangeListener?.invoke(this, position, true)
            }
        }
        return true
    }

    private fun inIndicatorArea(x: Float, y: Float): Boolean {
        if (x >= mIndicatorRect.left - TOUCH_AREA_EXPAND_SIZE
                && x <= mIndicatorRect.right + TOUCH_AREA_EXPAND_SIZE
                && y >= mIndicatorRect.top - TOUCH_AREA_EXPAND_SIZE
                && y <= mIndicatorRect.bottom + TOUCH_AREA_EXPAND_SIZE) {
            return true
        }
        return false
    }

    private fun startTouchAnimator() {
        mTouchAnimator?.cancel()
        val ani = createTouchAnimator()
        ani.start()
        mTouchAnimator = ani
    }

    private fun stopTouchAnimator() {
        mTouchAnimator?.cancel()
        mTouchAnimator = null
    }

    private fun createTouchAnimator(): Animator {
        val objectAnimator = ValueAnimator.ofFloat(1F, 0.98F)
        objectAnimator.addUpdateListener {
            val value = it.animatedValue as Float
            scaleX = value
            scaleY = value
        }
        objectAnimator.interpolator = BounceInterpolator()
        objectAnimator.duration = 500
        objectAnimator.repeatMode = ObjectAnimator.REVERSE
        objectAnimator.repeatCount = ObjectAnimator.INFINITE
        objectAnimator.addListener(object : AnimatorListenerAdapter() {
            override fun onAnimationCancel(animation: Animator?) {
                scaleX = 1F
                scaleY = 1F
            }
        })
        return objectAnimator
    }

}